package model.editormodel;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import model.LogicType;

/**
 * <b>In Verbindung stehende Klassen:</b> <code>LogicEditorElement, Input
 * </code><br>
 * <b>Funktion:</b> Umsetzung eines Ausgangs in einer Komponente<br>
 * <br>
 * Diese Klasse dient zur Repraesentation eines Ausgangs auf einem Bauteil. Die
 * <code>Output</code>-Klasse speichert eine <code>Input</code>-Liste mit denen
 * es verbunden ist, welche sozusagen die Senke darstellen. Ausserdem existiert
 * eine Referenz auf sein Wirtelement (<code>LogicEditorElement</code>) auf dem
 * sich der Output befindet.
 * 
 * @author Team Locsi
 * 
 */
public class Output {

	public final static String STD_O = "o";
	public final static String FF_Q = "q";
	public final static String FF_NQ = "nq";

	private String name;
	private List<Input> targetInputs;
	private LogicEditorElement myElement;
	private double xRelToElement;
	private double yRelToElement;

	private LogicEditorElement mySDElement;

	/**
	 * Dieser Konstruktor erzeugt ein neues <code>Output</code>-Objekt.
	 * 
	 * @param myElement
	 *            <code>LogicEditorElement</code> - Referenz auf das zugehoerige
	 *            <code>LogicEditorElement</code>
	 * @throws <code>IllegalArgumetnException</code>, falls null uebergeben wird
	 */
	public Output(LogicEditorElement myElement) {
		if (myElement == null) {
			throw new IllegalArgumentException("myElement darf nicht null sein");
		}
		this.myElement = myElement;
		targetInputs = new ArrayList<Input>();
		initializeCoordinates();

	}

	/**
	 * Diese Methode generiert an Hand des Typs von dem uebergebenen myElement,
	 * welchen Namen das Output-Objekt erhaelt. Dabei wird nur geprueft ob es
	 * sich dabei um das erste oder zweite Output Objekt handelt. Es prueft zum
	 * Beispiel nicht ob die Anzahl der Output-Objekte eines Elements bereits
	 * erschoepft ist.
	 */
	private void initializeCoordinates() {
		if (myElement.getType() != LogicType.FLIPFLOP) {
			this.name = Output.STD_O;
		} else {
			if (!myElement.getOutputs().containsKey(Output.FF_Q)) {
				this.name = Output.FF_Q;
			} else {
				this.name = Output.FF_NQ;
			}
		}

		// mit der Methode getStandardOutputCoordinate() wird an Hand des 
		// gesetzten Namens die zugehoerigen
		// Koordinaten ermittelt
		int[] coords = myElement.getType().getStandardOutputCoordinate(
				this.name);
		xRelToElement = coords[0];
		yRelToElement = coords[1];
	}

	/**
	 * Setzt eines der zugehoerigen <code>Input</code>-Objekte.
	 * 
	 * @param targetInput
	 *            <code>Input</code> - Referenz auf den zugehoerigen
	 *            <code>Input</code>
	 * @throws <code>IllegalArgumetnException</code>, falls null uebergeben wird
	 */
	protected void setTargetInput(Input targetInput) {
		if (targetInput == null) {
			throw new IllegalArgumentException(
					"targetInput darf nicht null sein");
		} else {
			targetInputs.add(targetInput);
		}
	}

	/**
	 * Entfernt das uebergebene <code>Input</code>-Objekt aus der Liste
	 * 
	 * @param targetInput
	 *            zu loeschendes <code>Input</code>-Objekt
	 * 
	 */
	protected void removeTargetInput(Input targetInput) {
		targetInputs.remove(targetInput);
	}

	/**
	 * Gibt die zugehoerigen <code>Input</code>-Objekte zurueck.
	 * 
	 * @return <code>List</code> - die zugehoerigen <code>Input</code>-Objekte.
	 */
	public List<Input> getTargetInputs() {
		return targetInputs;
	}

	/**
	 * Gibt das zugehoerige <code>LogicEditorElement</code> zurueck.
	 * 
	 * @return <code>LogicEditorElement</code> - das zugehoerige
	 *         <code>LogicEditorElement</code>.
	 */
	public LogicEditorElement getMySuperElement() {
		if (mySDElement != null) {
			return mySDElement;
		} else {
			return myElement;
		}
	}

	/**
	 * Setzt das SuperElement des Outputs (selbstdefiniertes Bauteil)
	 * 
	 * @param mySDElement <code>LogicEditorElement</code>
	 */
	public void setMySuperElement(LogicEditorElement mySDElement) {
		this.mySDElement = mySDElement;
	}

	/**
	 * Methode, die immer das Ursprungselement zurueckgibt auf dem es liegt, auch
	 * wenn es jetzt auf einem selbstdefinierten Bauteil liegt
	 * 
	 * @return LogicEditorElement
	 */
	public LogicEditorElement getMyElement() {
		return myElement;
	}

	/**
	 * Gibt den Namen des <code>Output</code> zurueck.
	 * 
	 * @return <code>String</code> - den Namen des <code>Output</code>
	 */
	public String getName() {
		return name;
	}

	/**
	 * Gibt den Namen des Ausgangs zurueck den er nach aussen hat. Der ist
	 * eventuell nicht mehr der standardname, weil er als Ausgang eines
	 * selbstdefinierten bauteils dient. sonst den standardnamen
	 * 
	 * @return <code>String</code> - Name des Ausgangs
	 */
	public String getOuterName() {
		if (mySDElement != null) {
			for (Map.Entry<String, Output> ent : mySDElement.getOutputs()
					.entrySet()) {
				if (ent.getValue().equals(this)) {
					return ent.getKey();
				}
			}
		}
		return name;
	}

	/**
	 * Setzt die x-Koordinate des <code>Output</code> in Pixel, relativ zum
	 * zugehoerigen <code>LogicEditorElement</code>.
	 * 
	 * @param xRelToElement
	 *            <code>double</code> - x-Koordinate des <code>Output</code> in
	 *            Pixel
	 */
	protected void setXRelToElement(double xRelToElement) {
		this.xRelToElement = xRelToElement;
	}

	/**
	 * Gibt die x-Koordinate des <code>Output</code> in Pixel, relativ zum
	 * zugehoerigen <code>LogicEditorElement</code>, zurueck.
	 * 
	 * @return <code>double</code> - die x-Koordinate des <code>Output</code> in
	 *         Pixel
	 */
	public double getXRelToElement() {
		return xRelToElement;
	}

	/**
	 * Setzt die y-Koordinate des <code>Output</code> in Pixel, relativ zum
	 * zugehoerigen <code>LogicEditorElement</code>.
	 * 
	 * @param yRelToElement
	 *            <code>double</code> - y-Koordinate des <code>Output</code> in
	 *            Pixel
	 */
	protected void setYRelToElement(double yRelToElement) {
		this.yRelToElement = yRelToElement;
	}

	/**
	 * Gibt die y-Koordinate des <code>Output</code> in Pixel, relativ zum
	 * zugehoerigen <code>LogicEditorElement</code>, zurueck.
	 * 
	 * @return <code>double</code> - die y-Koordinate des <code>Output</code> in
	 *         Pixel
	 */
	public double getYRelToElement() {
		return yRelToElement;
	}
}